using Common.Logging;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Linq;

namespace Hinacom.Scheduler.Service
{
    /// <summary>
    /// The main server logic.
    /// </summary>
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
    public class SchedulerService : ISchedulerService
	{
		private readonly ILog logger;
		private ISchedulerFactory schedulerFactory;
		private IScheduler scheduler;

        /// <summary>
        /// Initializes a new instance of the <see cref="SchedulerService"/> class.
        /// </summary>
	    public SchedulerService()
	    {
	        logger = LogManager.GetLogger(GetType());
	    }

	    /// <summary>
		/// Initializes the instance of the <see cref="SchedulerService"/> class.
		/// </summary>
		public virtual void Initialize()
		{
			try
			{
				schedulerFactory = CreateSchedulerFactory();
				scheduler = GetScheduler();
			}
			catch (Exception e)
			{
				logger.Error("Server initialization failed:" + e.Message, e);
				throw;
			}
		}

        /// <summary>
        /// Gets the scheduler with which this server should operate with.
        /// </summary>
        /// <returns></returns>
	    protected virtual IScheduler GetScheduler()
	    {
	        return schedulerFactory.GetScheduler();
	    }

        /// <summary>
        /// Returns the current scheduler instance (usually created in <see cref="Initialize" />
        /// using the <see cref="GetScheduler" /> method).
        /// </summary>
	    protected virtual IScheduler Scheduler
	    {
	        get { return scheduler; }
	    }

	    /// <summary>
        /// Creates the scheduler factory that will be the factory
        /// for all schedulers on this instance.
        /// </summary>
        /// <returns></returns>
	    protected virtual ISchedulerFactory CreateSchedulerFactory()
	    {
	        return new StdSchedulerFactory();
	    }

	    /// <summary>
		/// Starts this instance, delegates to scheduler.
		/// </summary>
		public virtual void Start()
		{
	        try
	        {
	            scheduler.Start();
	        }
	        catch (Exception ex)
	        {
	            logger.Fatal(string.Format("Scheduler start failed: {0}", ex.Message), ex);
	            throw;
	        }

			logger.Info("Scheduler started successfully");
		}

		/// <summary>
		/// Stops this instance, delegates to scheduler.
		/// </summary>
		public virtual void Stop()
		{
            try
            {
                scheduler.Shutdown(true);
            }
            catch (Exception ex)
            {
                logger.Error(string.Format("Scheduler stop failed: {0}", ex.Message), ex);
                throw;
            }

			logger.Info("Scheduler shutdown complete");
		}

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
	    public virtual void Dispose()
	    {
	        // no-op for now
	    }

        /// <summary>
        /// Pauses all activity in scheduler.
        /// </summary>
	    public virtual void Pause()
	    {
	        scheduler.PauseAll();
	    }

        /// <summary>
        /// Resumes all activity in server.
        /// </summary>
	    public void Resume()
	    {
	        scheduler.ResumeAll();
	    }
        /// <summary>
        /// Creates a job in scheduler.
        /// </summary>
        /// <param name="job">The information and schedule of the job.</param>
        /// <param name="overwrite">Whether to overwrite the job if the name exists.</param>
        public void CreateJob(JobInfo job, bool overwrite)
        {
            try
            {
                IJobDetail jobDetail = null;
                ITrigger trigger = null;
                JobInfoConverter.ToQuartzJobDetailAndTrigger(job, out jobDetail, out trigger);

                var existing = scheduler.GetJobDetail(JobKey.Create(job.Name, job.Group));
                if (existing != null)
                {
                    if (overwrite)
                        scheduler.DeleteJob(existing.Key);
                    else
                        throw new Exception("A job with same name in the same group exists. Creation failed.");
                }

                scheduler.ScheduleJob(jobDetail, trigger);
            }
            catch (Exception ex)
            {
                logger.Error("An error occurred when creating a job.", ex);
                throw ex;
            }
        }
        /// <summary>
        /// Deletes a job from scheduler.
        /// </summary>
        /// <param name="jobName">The name of the job.</param>
        /// <param name="jobGroup">The group that the job belongs to.</param>
        public void DeleteJob(string jobName, string jobGroup)
        {
            try
            {
                scheduler.DeleteJob(JobKey.Create(jobName, jobGroup));
            }
            catch (Exception ex)
            {
                logger.Error("An error occurred when deleting a job.", ex);
                throw ex;
            }
        }
        /// <summary>
        /// Gets all active jobs in scheduler.
        /// </summary>
        /// <returns>A list of JobInfo objects.</returns>
        public IEnumerable<JobInfo> GetActiveJobs()
        {
            List<JobInfo> jobInfos = new List<JobInfo>();
            var groupNames = scheduler.GetJobGroupNames();
            foreach (string groupName in groupNames)
            {
                GroupMatcher<JobKey> matcher = GroupMatcher<JobKey>.GroupEquals(groupName);
                var jobkeys = scheduler.GetJobKeys(matcher);
                foreach (JobKey key in jobkeys)
                {
                    JobInfo job = null;
                    IJobDetail detail = scheduler.GetJobDetail(key);
                    var trigger = scheduler.GetTriggersOfJob(key)[0];

                    JobInfoConverter.ToJobInfo(detail, trigger, out job);

                    jobInfos.Add(job);
                }
            }

            return jobInfos;
        }
        /// <summary>
        /// Gets the running history of a specific job.
        /// </summary>
        /// <param name="jobName">The name of the job.</param>
        /// <param name="jobGroup">The group to that the job belongs.</param>
        /// <returns>The running history of the job.</returns>
        public IEnumerable<JobHistory> GetJobHistories(string jobName, string jobGroup, int? lastLoadedId)
        {
            return JobStoreExFactory.GetJobStoreExt().GetJobHistories(jobName, jobGroup, lastLoadedId);
        }
        /// <summary>
        /// Gets the detail log of a job running history record.
        /// </summary>
        /// <param name="historyId">The id of the history</param>
        /// <returns>The detial running log.</returns>
        public string GetJobHistoryLog(int historyId)
        {
            return JobStoreExFactory.GetJobStoreExt().GetJobHistoryLog(historyId);
        }
    }
}
